package com.vitogiovanni.tobook.entity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Nullable;
import javax.inject.Named;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.persistence.EntityNotFoundException;

import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiMethod.HttpMethod;
import com.google.api.server.spi.config.ApiNamespace;
import com.google.api.server.spi.response.CollectionResponse;
import com.google.appengine.api.datastore.Cursor;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.datanucleus.query.JDOCursorHelper;
import com.vitogiovanni.tobook.utilities.JsonParser;


@Api(name = "localeendpoint", namespace = @ApiNamespace(ownerDomain = "vitogiovanni.com", ownerName = "vitogiovanni.com", packagePath = "tobook.entity"))
public class LocaleEndpoint {
	/**
	 * This method lists all the entities inserted in datastore. It uses HTTP
	 * GET method and paging support.
	 * 
	 * @return A CollectionResponse class containing the list of all entities
	 *         persisted and a cursor to the next page.
	 */
	@SuppressWarnings({ "unchecked", "unused" })
	@ApiMethod(name = "listLocale")
	public CollectionResponse<Locale> listLocale(
			@Nullable @Named("cursor") String cursorString,
			@Nullable @Named("limit") Integer limit) {

		PersistenceManager mgr = null;
		Cursor cursor = null;
		List<Locale> execute = null;

		try {
			mgr = getPersistenceManager();
			Query query = mgr.newQuery(Locale.class);
			if (cursorString != null && cursorString != "") {
				cursor = Cursor.fromWebSafeString(cursorString);
				HashMap<String, Object> extensionMap = new HashMap<String, Object>();
				extensionMap.put(JDOCursorHelper.CURSOR_EXTENSION, cursor);
				query.setExtensions(extensionMap);
			}

			if (limit != null) {
				query.setRange(0, limit);
			}

			execute = (List<Locale>) query.execute();
			cursor = JDOCursorHelper.getCursor(execute);
			if (cursor != null)
				cursorString = cursor.toWebSafeString();

			// Tight loop for fetching all entities from datastore and
			// accomodate
			// for lazy fetch.
			for (Locale obj : execute)
				;
		} finally {
			mgr.close();
		}

		return CollectionResponse.<Locale> builder().setItems(execute)
				.setNextPageToken(cursorString).build();
	}

	/**
	 * This method gets the entity having primary key id. It uses HTTP GET
	 * method.
	 * 
	 * @param id
	 *            the primary key of the java bean.
	 * @return The entity with primary key id.
	 */
	@ApiMethod(name = "getLocale", path = "get_locale")
	public Locale getLocale(@Named("id")long id) {
		PersistenceManager mgr = getPersistenceManager();
		Locale locale = null;
		try {
			locale = mgr.getObjectById(Locale.class, id);
			locale.getAvatar();
			locale.getOrariLocale();
		} catch (javax.jdo.JDOObjectNotFoundException notFound) {
			locale = null;
		} finally {
			mgr.close();
		}

		return locale;
	}
	
	@SuppressWarnings({"unused", "unchecked"})
	@ApiMethod(name = "getLocali", httpMethod=HttpMethod.GET, path = "get_locali")
	public CollectionResponse<Locale> getLocali(
			@Named("coordinataX") double coordinataX,
			@Named("coordinataY") double coordinataY, 
			@Named("raggio") double raggio, 
			@Named("tipologieLocale")ArrayList<String> tipologie,
			@Named("servizi")ArrayList<String> servizi,
			@Nullable @Named("cursor") String cursorString,
			@Nullable @Named("limit") Integer limit) {
		PersistenceManager mgr = null;
		Cursor cursor = null;
		List<Locale> execute = new ArrayList<Locale>();

		try {
			mgr = getPersistenceManager();
			Query queryX = mgr.newQuery(Locale.class);
			
			if (cursorString != null && cursorString != "") {
				cursor = Cursor.fromWebSafeString(cursorString);
				HashMap<String, Object> extensionMap = new HashMap<String, Object>();
				extensionMap.put(JDOCursorHelper.CURSOR_EXTENSION, cursor);
				queryX.setExtensions(extensionMap);
			}

			if (limit != null) {
				queryX.setRange(0, limit);
			}
			
			double diffX, sumX, diffY, sumY;
			//trovo deltaLat e deltaLong in base al raggio (in km) fornito, sfruttando il seguente calcolo:
			double kmInLongitudeDegree = 111.320 * Math.cos( coordinataY / 180.0 * Math.PI); 
			double deltaLat = raggio / 111.1;
			double deltaLong = raggio / kmInLongitudeDegree;
			
			diffX = coordinataX - deltaLat;
			sumX = coordinataX + deltaLat;
			diffY = coordinataY - deltaLat;
			sumY = coordinataY + deltaLat;
			HashMap<String, Object> params = new HashMap<String, Object>();
			params.put("diffX", diffX);
			params.put("sumX", sumX);
			// Implementazione filtri
			queryX.declareParameters("double diffX, double sumX");
			//query.declareParameters("double coordY");
			//query.declareParameters("double raggio");
			// Filtro coordinate - raggio
			
			queryX.setFilter("coordinataX >= diffX && coordinataX <= sumX");
			//query.setFilter("coordinataX >= diffX && coordinataX <= sumX && coordinataY >= diffY && coordinataY <= sumY");
			//query.setFilter("coordinataX <= sumX");
			//query.setFilter("coordinataY >= diffY");
			//query.setFilter("coordinataY <= sumY");
			// GLI ALTRI FILTRI LI APPLICO DOPO AVER TROVATO I LOCALI NELLA ZONA

			List<Locale> temp = (List<Locale>) queryX.executeWithMap(params);

			// Filtro tipologieLocale
			// DA IMPLEMENTARE
			// Filtro servizi
			// DA IMPLEMENTARE
			
			for (Locale locale : temp) {
				boolean serviziOK = true;
				boolean tipologieOK = false;
				Set<Key> serviziLocKey = locale.getServizi();
				Set<String> serviziLoc = new HashSet<String>();
				for(Key servKey: serviziLocKey)
				{
					serviziLoc.add(servKey.getName());
				}
				Set<Key> tipologieLocKey = locale.getTipologia();
				Set<String> tipologieLoc = new HashSet<String>();
				for(Key tipoKey: tipologieLocKey)
				{
					tipologieLoc.add(tipoKey.getName());
				}
				if (servizi != null && servizi.size() > 0) {
					if(servizi.get(0).equals("no"))
					{
						serviziOK = true;
					}
					else{				
					for (String serv : servizi) {
						if (!serviziLoc.contains(serv)) {
							serviziOK = false;
						}
					}
					}
				} else {
					serviziOK = true;
				}

				
				if (tipologie != null && tipologie.size() > 0) {
					if(tipologie.get(0).equals("no")){
						tipologieOK = true;
						}
					else{
					for (String tipo : tipologie) {
						if (tipologieLoc.contains(tipo)) {
							tipologieOK = true;
						}
					}
				}} else {
					tipologieOK = true;
				}

				if(serviziOK && tipologieOK)
				{
					if(locale.getCoordinataY()>= diffY && locale.getCoordinataY()<=sumY)
					{
						execute.add(locale);
					}
				}
			}

			cursor = JDOCursorHelper.getCursor(execute);
			if (cursor != null)
				cursorString = cursor.toWebSafeString();

			// Tight loop for fetching all entities from datastore and
			// accomodate
			// for lazy fetch.
			for (Locale obj : execute){
				obj.getTipologia();
				Foto foto = obj.getAvatar();
				if(foto != null)
				{
					foto.getFoto();
				}
			}
		} finally {
			mgr.close();
		}

		return CollectionResponse.<Locale> builder().setItems(execute)
				.setNextPageToken(cursorString).build();
	}

	/**
	 * This inserts a new entity into App Engine datastore. If the entity
	 * already exists in the datastore, an exception is thrown. It uses HTTP
	 * POST method.
	 * 
	 * @param locale
	 *            the entity to be inserted.
	 * @return The inserted entity.
	 */

	@ApiMethod(name = "insertLocale")
	public Locale insertLocale(Locale locale) {
		PersistenceManager mgr = getPersistenceManager();
		try {
			/*if (containsLocale(locale)) {
				throw new EntityExistsException("Object already exists");
			}*/
			mgr.makePersistent(locale);
		} finally {
			mgr.close();
		}
		return locale;
	}

	/**
	 * This method is used for updating an existing entity. If the entity does
	 * not exist in the datastore, an exception is thrown. It uses HTTP PUT
	 * method.
	 * 
	 * @param locale
	 *            the entity to be updated.
	 * @return The updated entity.
	 */
	@ApiMethod(name = "updateLocale")
	public Locale updateLocale(Locale locale) {
		PersistenceManager mgr = getPersistenceManager();
		try {
			if (!containsLocale(locale)) {
				throw new EntityNotFoundException("Object does not exist");
			}
			mgr.makePersistent(locale);
		} finally {
			mgr.close();
		}
		return locale;
	}

	/**
	 * This method removes the entity with primary key id. It uses HTTP DELETE
	 * method.
	 * 
	 * @param id
	 *            the primary key of the entity to be deleted.
	 */
	@ApiMethod(name = "removeLocale")
	public void removeLocale(@Named("id") Long id) {
		PersistenceManager mgr = getPersistenceManager();
		try {
			Locale locale = mgr.getObjectById(Locale.class, id);
			mgr.deletePersistent(locale);
		} finally {
			mgr.close();
		}
	}
	
	@SuppressWarnings({"finally", "unchecked" })
	@ApiMethod(name = "findByName", path="find_by_name", httpMethod=HttpMethod.GET)
	public Locale findByName(@Named("nome") String nome) {

		Locale localeB = null;
		PersistenceManager mgr = getPersistenceManager();
		List<Locale> locale = null;
		Set<Foto> listFoto = null;
		Set<OrariApertura> listOrariApertura = null;
		// List<Gestore> gestore = new List<Gestore>();
		try {
			Query query = mgr.newQuery(Locale.class);
			query.setFilter("nome == thatNome");
			query.declareParameters("String thatNome");
			locale = (List<Locale>) query.execute(nome);
			localeB = locale.get(0);
			listFoto = localeB.getFoto();
			for(Foto fotoIndex:  listFoto)
			{
				fotoIndex.getFoto();
			}
			localeB.getMenu();
			List<ElementoMenu> elemMenu = localeB.getMenu().getElementiMenu();
			for(ElementoMenu elem: elemMenu){
				elem.getIngredienti();
				elem.getNome();
				elem.getPrezzoElemento();
				elem.getTipoElemento().getNome();
				elem.getCategoriaElemento();
			}
			localeB.getTipologia();
			listOrariApertura = localeB.getOrariLocale();
			if(listOrariApertura != null)
			{
			for(OrariApertura temp: listOrariApertura){
				temp.getOrarioApertura();
				temp.getOrarioChiusura();
			}
			}

		} finally {
			mgr.close();
			return localeB;
		}

	}
	
	@SuppressWarnings({"finally"})
	@ApiMethod(name = "findByJsonKey", path="find_by_json_key", httpMethod=HttpMethod.GET)
	public Locale findByJsonKey(@Named("jsonString") String jsonString) {

		Locale locale = null;
		PersistenceManager mgr = getPersistenceManager();
		Set<Foto> listFoto = null;
		Set<OrariApertura> listOrariApertura = null;
		Key key = JsonParser.jsonToKey(jsonString);
		try {
			locale = mgr.getObjectById(Locale.class, key);
			listFoto = locale.getFoto();
			if(listFoto != null)
			{
				for(Foto fotoIndex:  listFoto)
				{
					fotoIndex.getFoto();
				}
			}
			if (locale.getMenu() != null) {
				List<ElementoMenu> elemMenu = locale.getMenu()
						.getElementiMenu();
				if (elemMenu != null) {
					for (ElementoMenu elem : elemMenu) {

						elem.getIngredienti();
						elem.getNome();
						elem.getPrezzoElemento();
						elem.getTipoElemento().getNome();
						elem.getCategoriaElemento();
					}
				}
			}
			locale.getTipologia();
			listOrariApertura = locale.getOrariLocale();
			
			if(listOrariApertura != null)
			{
				for(OrariApertura temp: listOrariApertura){
					temp.getOrarioApertura();
					temp.getOrarioChiusura();
				}
			}

		} finally {
			mgr.close();
			return locale;
		}

	}
	
	@ApiMethod(name = "findPreferitiUtente", httpMethod = HttpMethod.GET)
	public List<Locale> findPreferitiUtente(
			@Named("emailUtente") String emailUtente) {
		List<Locale> listaLocali = new ArrayList<Locale>();

		UtenteEndpoint utenteEndpoint = new UtenteEndpoint();
		Utente utente = utenteEndpoint.findByEmail(emailUtente);
		if(utente == null)
		{
			utenteEndpoint.insertUtente(new Utente(emailUtente));
			return listaLocali;
		}
		Set<Key> listaKeyLocali = utente.getLocaliPreferiti();
		PersistenceManager mgr = getPersistenceManager();
		try {
			for (Key keyIndex : listaKeyLocali) {
				Locale locale = null;
				locale = mgr.getObjectById(Locale.class, keyIndex);
				locale.getAvatar();
				listaLocali.add(locale);
			}
		} finally {
			mgr.close();
		}
		return listaLocali;
	}
	
	
	@SuppressWarnings("unchecked")
	@ApiMethod(name = "insertMenu", path = "insert_menu", httpMethod=HttpMethod.POST)
	public void insertMenu(Menu menu, @Named("nomeLocale")String nomeLocale)
	{
		PersistenceManager mgr = getPersistenceManager();
		Locale localeB = null;
		List<Locale> locale = null;
		try {
		Query query = mgr.newQuery(Locale.class);
		query.setFilter("nome == thatNome");
		query.declareParameters("String thatNome");
		locale = (List<Locale>) query.execute(nomeLocale);
		localeB = locale.get(0);
		localeB.setMenu(menu);
		
		mgr.makePersistent(localeB);
		
		}finally {
			mgr.close();
		}
		
		
	}
	
	@SuppressWarnings("unchecked")
	@ApiMethod(name = "insertAvatar", path="insert_avatar", httpMethod=HttpMethod.POST)
	public void insertAvatar(Foto foto, @Named("nomeLocale")String nomeLocale)
	{
		PersistenceManager mgr = getPersistenceManager();
		Locale localeB = null;
		List<Locale> locale = null;
		try {
		Query query = mgr.newQuery(Locale.class);
		query.setFilter("nome == thatNome");
		query.declareParameters("String thatNome");
		locale = (List<Locale>) query.execute(nomeLocale);
		localeB = locale.get(0);
		localeB.setAvatar(foto);
		
		mgr.makePersistent(localeB);
		
		}finally {
			mgr.close();
		}
		
		
	}
	
	@SuppressWarnings("unchecked")
	@ApiMethod(name = "insertFotoLocale", path = "insert_foto_locale", httpMethod=HttpMethod.POST)
	public void insertFotoLocale(Foto blob, @Named("nomeLocale")String nomeLocale)
	{
		PersistenceManager mgr = getPersistenceManager();
		Locale localeB = null;
		Set<Foto> fotoInLocale;
		List<Locale> locale = null;
		try {
		Query query = mgr.newQuery(Locale.class);
		query.setFilter("nome == thatNome");
		query.declareParameters("String thatNome");
		locale = (List<Locale>) query.execute(nomeLocale);
		localeB = locale.get(0);
		fotoInLocale = localeB.getFoto();
		
		fotoInLocale.add(blob);
		
		mgr.makePersistent(localeB);
		
		}finally {
			mgr.close();
		}
		
		
	}
	
	@SuppressWarnings("unchecked")
	@ApiMethod(name = "insertOrarioLocale", path = "insert_orario_locale", httpMethod=HttpMethod.POST)
	public void insertOrarioLocale(OrariApertura orario, @Named("nomeLocale")String nomeLocale)
	{
		PersistenceManager mgr = getPersistenceManager();
		Locale localeB = null;
		Set<OrariApertura> orariLocale;
		List<Locale> locale = null;
		try {
		Query query = mgr.newQuery(Locale.class);
		query.setFilter("nome == thatNome");
		query.declareParameters("String thatNome");
		locale = (List<Locale>) query.execute(nomeLocale);
		localeB = locale.get(0);
		orariLocale = localeB.getOrariLocale();
		
		if(orariLocale == null)
		{
			orariLocale = new HashSet<OrariApertura>();
		}
		orariLocale.add(orario);
		localeB.setOrariLocale(orariLocale);
		mgr.makePersistent(localeB);
		
		}finally {
			mgr.close();
		}
		
		
	}
	
	private boolean containsLocale(Locale locale) {
		PersistenceManager mgr = getPersistenceManager();
		boolean contains = true;
		try {
			mgr.getObjectById(Locale.class, locale.getId());
		} catch (javax.jdo.JDOObjectNotFoundException ex) {
			contains = false;
		} finally {
			mgr.close();
		}
		return contains;
	}

	private static PersistenceManager getPersistenceManager() {
		return PMF.get().getPersistenceManager();
	}
	
}
